package mspbots.qbd.desktop.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.qbd.common.RabbitKeys;
import mspbots.qbd.desktop.core.customers.CustomersService;
import mspbots.qbd.desktop.core.profitandloss.*;
import org.json.JSONObject;
import org.json.XML;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.time.LocalDate;


/**
 * mspbots.data.quickbooks.ws.ResultResponeService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2020/5/28
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class ResponseResultService extends BaseGenericService {

    private final ProfitAndLossService profitAndLossService;
    private final BalanceSheetService balanceSheetService;
    private final InvoiceService invoiceService;
    private final CustomersService customersService;
    private final BillService billService;
    private final JournalService journalService;

    public void buildResult(String source, SoapSession session) throws ParserConfigurationException, IOException, SAXException {
        Long tenantId = session.getTenantId();
        Document document = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder().parse(new InputSource(new StringReader(source)));
        if (session.getIndex() == RequestDataType.ACCOUNT) {
            NodeList nodeList = document.getElementsByTagName("AccountRet");
            this.sendAccount(nodeList, tenantId);
            session.setIndex(session.getIndex() + 1);
        } else if (session.getIndex() == RequestDataType.CUSTOMER) {
            NodeList nodeList = document.getElementsByTagName("CustomerRet");
            this.customersService.sendCustomers(nodeList, tenantId);
            if (this.setSession(document.getElementsByTagName("CustomerQueryRs"), session) == 0) {
                session.setIndex(session.getIndex() + 1);
                session.setAction("Start");
                session.setIteratorId(null);
            }
        } else if (session.getIndex() == RequestDataType.PROFIT_AND_LOSS_STANDARD) {
            NodeList nodeList = document.getElementsByTagName("ReportRet");
            this.profitAndLossService.sendStandard(nodeList, tenantId);
            session.setStartDate(session.getStartDate().plusMonths(1));
            if (session.getStartDate().isAfter(LocalDate.now())) {
                session.setIndex(session.getIndex() + 1);
                session.setStartDate(LocalDate.now().minusYears(WebServiceConfig.SYNC_DATA_MINUS_YEARS));
            }
        } else if (session.getIndex() == RequestDataType.PROFIT_AND_LOSS_DETAIL) {
            NodeList nodeList = document.getElementsByTagName("ReportRet");
            this.profitAndLossService.sendDetail(nodeList, tenantId);
            session.setStartDate(session.getStartDate().plusMonths(1));
            if (session.getStartDate().isAfter(LocalDate.now())) {
                session.setIndex(session.getIndex() + 1);
                session.setStartDate(LocalDate.now().minusYears(WebServiceConfig.SYNC_DATA_MINUS_YEARS));
            }
        } else if (session.getIndex() == RequestDataType.BALANCE_SHEET_SUMMARY) {
            NodeList nodeList = document.getElementsByTagName("ReportRet");
            this.balanceSheetService.sendSummary(nodeList, tenantId);
            session.setStartDate(session.getStartDate().plusMonths(1));
            if (session.getStartDate().isAfter(LocalDate.now())) {
                session.setIndex(session.getIndex() + 1);
                session.setStartDate(LocalDate.now().minusYears(WebServiceConfig.SYNC_DATA_MINUS_YEARS));
            }
        } else if (session.getIndex() == RequestDataType.BALANCE_SHEET_DETAIL) {
            NodeList nodeList = document.getElementsByTagName("ReportRet");
            this.balanceSheetService.sendDetail(nodeList, tenantId);
            session.setStartDate(session.getStartDate().plusMonths(1));
            if (session.getStartDate().isAfter(LocalDate.now())) {
                session.setIndex(session.getIndex() + 1);
                session.setStartDate(LocalDate.now().minusYears(WebServiceConfig.SYNC_DATA_MINUS_YEARS));
            }
        } else if (session.getIndex() == RequestDataType.PROFIT_AND_LOSS_PREV_YEAR_COMP) {
            NodeList nodeList = document.getElementsByTagName("ReportRet");
            this.profitAndLossService.sendPrevYearComp(nodeList, tenantId);
            session.setStartDate(session.getStartDate().plusMonths(1));
            if (session.getStartDate().isAfter(LocalDate.now())) {
                session.setIndex(session.getIndex() + 1);
                session.setStartDate(LocalDate.now().minusYears(WebServiceConfig.SYNC_DATA_MINUS_YEARS));
            }
        } else if (session.getIndex() == RequestDataType.PROFIT_AND_LOSS_YTD_COMP) {
            NodeList nodeList = document.getElementsByTagName("ReportRet");
            this.profitAndLossService.sendYTDComp(nodeList, tenantId);
            session.setStartDate(session.getStartDate().plusMonths(1));
            if (session.getStartDate().isAfter(LocalDate.now())) {
                session.setIndex(session.getIndex() + 1);
                session.setStartDate(LocalDate.now().minusYears(WebServiceConfig.SYNC_DATA_MINUS_YEARS));
            }
        } else if (session.getIndex() == RequestDataType.INVOICE) {
            NodeList nodeList = document.getElementsByTagName("InvoiceRet");
            this.invoiceService.sendInvoice(nodeList, tenantId);
            if (this.setSession(document.getElementsByTagName("InvoiceQueryRs"), session) == 0) {
                session.setIndex(session.getIndex() + 1);
                session.setAction("Start");
                session.setIteratorId(null);
            }
        } else if (session.getIndex() == RequestDataType.BILL) {
            NodeList nodeList = document.getElementsByTagName("BillRet");
            this.billService.sendBill(nodeList, tenantId);
            if (this.setSession(document.getElementsByTagName("BillQueryRs"), session) == 0) {
                session.setIndex(session.getIndex() + 1);
                session.setAction("Start");
                session.setIteratorId(null);
            }
        } else if (session.getIndex() == RequestDataType.JOURNAL) {
            NodeList nodeList = document.getElementsByTagName("JournalEntryRet");
            this.journalService.sendJournal(nodeList, tenantId);
            if (this.setSession(document.getElementsByTagName("JournalEntryQueryRs"), session) == 0) {
                session.setIndex(session.getIndex() + 1);
                session.setAction("Start");
                session.setIteratorId(null);
            }
        } else {
            session.setIndex(session.getIndex() + 1);
        }

        log.debug("BuildResult SoapSession json str {}", session);

    }

    public void sendAccount(NodeList nodeList, Long tenantId) {
        for (int temp = 0; temp < nodeList.getLength(); temp++) {
            Node node = nodeList.item(temp);
            NodeList el = ((Element) node).getElementsByTagName("ListID");
            String listId = el.item(0).getTextContent();
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                String content = this.nodeToString(node);
                JSONObject jsonObject = XML.toJSONObject(content);
                ObjectNode objectNode = super.objectMapper.createObjectNode();
                try {
                    objectNode = super.objectMapper.readValue(jsonObject.get("AccountRet").toString(), ObjectNode.class);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                objectNode.put("tenantId", tenantId);
                objectNode.put("ListID", listId);
                this.messagingTemplate.convertAndSend(RabbitKeys.BASE_EXCHANGE_NAME,
                        RabbitKeys.ACCOUNT_SYNC_QUEUE_KEY, objectNode);
            }
        }
    }

    private int setSession(NodeList nodeList, SoapSession session) {
        String countStr = "0";
        String iteratorStr = null;
        for (int x = 0; x < nodeList.getLength(); x++) {
            Node node = nodeList.item(x);
            countStr = node.getAttributes().getNamedItem("iteratorRemainingCount").getNodeValue();
            iteratorStr = node.getAttributes().getNamedItem("iteratorID").getNodeValue();
        }
        session.setIteratorId(iteratorStr);
        int count = Integer.parseInt(countStr);
        if (count == 0) {
            session.setAction("Stop");
        } else {
            session.setAction("Continue");
        }
        return count;
    }

    private String nodeToString(Node node) {
        StringWriter sw = new StringWriter();
        try {
            Transformer t = TransformerFactory.newInstance().newTransformer();
            t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            t.setOutputProperty(OutputKeys.INDENT, "yes");
            t.transform(new DOMSource(node), new StreamResult(sw));
        } catch (TransformerException te) {
            System.out.println("nodeToString Transformer Exception");
        }
        return sw.toString();
    }
}
